package com.sharyuke.viam.utils

import android.annotation.SuppressLint
import android.app.Activity
import android.content.Context
import android.content.res.ColorStateList
import android.graphics.Rect
import android.os.Handler
import android.os.Looper
import android.text.Editable
import android.text.TextWatcher
import android.text.method.HideReturnsTransformationMethod
import android.text.method.LinkMovementMethod
import android.text.method.PasswordTransformationMethod
import android.text.util.Linkify
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import android.view.animation.Animation
import android.view.inputmethod.EditorInfo
import android.view.inputmethod.InputMethodManager
import android.widget.CompoundButton
import android.widget.EditText
import android.widget.ImageView
import android.widget.SeekBar
import android.widget.TextView
import androidx.annotation.IdRes
import androidx.core.view.isVisible
import androidx.lifecycle.LifecycleCoroutineScope
import androidx.lifecycle.findViewTreeLifecycleOwner
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.Job
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.channels.trySendBlocking
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.launch


val uiHandler = Handler(Looper.getMainLooper())

val View.lifecycleScope get() = findViewTreeLifecycleOwner()?.lifecycleScope
fun View.lifecycleScope(block: LifecycleCoroutineScope.() -> Unit) =
    findViewTreeLifecycleOwner()?.lifecycleScope?.apply(block)

fun <T> Flow<T>.launchIn(scope: CoroutineScope?): Job? = scope?.launch(CoroutineExceptionHandler { _, e -> e.printStackTrace() }) { collect() }

fun <T : View> T.onClickEnable(enable: Boolean = false, back: T.() -> Unit): T = apply {
    if (!enable) setOnClickListener { back() }
    else {
        setOnClickListener(null)
        isClickable = false
        isFocusableInTouchMode = false
        isFocusable = false
    }
}

val Context.imm get() = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager


val Int?.floatValue get() = this?.runCatching { toFloat() }?.getOrNull() ?: 0f
val Double?.floatValue get() = this?.runCatching { toFloat() }?.getOrNull() ?: 0f
fun View.setBgColor(color: Int) = setBackgroundColor(resources.getColor(color))

@SuppressLint("ClickableViewAccessibility")
fun ViewGroup.fixFromScroll() {
    setOnTouchListener { _, event ->
        requestDisallowInterceptTouchEvent(event.action != MotionEvent.ACTION_UP)
        false
    }
}


/**
 * 隐藏或者显示
 */
fun View.toggleVisible() = let { isVisible = !isVisible }

/**
 * 切换是否选中
 */
fun View.toggleSelect() = let { isSelected = !isSelected }
fun EditText.onSend(block: (String) -> Unit) {
    setOnEditorActionListener { v, actionId, _ ->
        if (actionId == EditorInfo.IME_ACTION_SEND) block(v.text.toString())
        true
    }
}

fun EditText.onSearch(block: EditText.(String) -> Unit) {
    setOnEditorActionListener { v, actionId, _ ->
        if (actionId == EditorInfo.IME_ACTION_SEARCH) {
            block(v.text.toString())
            context.hideSoftKeyboard(v)
        }
        true
    }
}

/**
 * 隐藏输入法 软键盘
 */
fun Context.hideSoftKeyboard(view: View? = null) {
    val manager = getSystemService(Activity.INPUT_METHOD_SERVICE) as InputMethodManager
    manager.hideSoftInputFromWindow(view?.windowToken, InputMethodManager.HIDE_NOT_ALWAYS)
}

fun Animation.onEnd(block: Animation.() -> Unit) {
    setAnimationListener(object : Animation.AnimationListener {
        override fun onAnimationStart(animation: Animation?) {
        }

        override fun onAnimationEnd(animation: Animation?) = block()

        override fun onAnimationRepeat(animation: Animation?) {
        }
    })
}

fun ImageView.tint(tint: Int) = ColorStateList.valueOf(tint).let { imageTintList = it }

@SuppressLint("UseCompatTextViewDrawableApis")
fun TextView.tint(tint: Int) = ColorStateList.valueOf(tint).let { compoundDrawableTintList = it }

fun EditText.pwdShow() = PasswordTransformationMethod.getInstance().let { transformationMethod = it }

fun EditText.pwdHide() = HideReturnsTransformationMethod.getInstance().let { transformationMethod = it }

fun TextView.highLightLink() = apply { autoLinkMask = Linkify.WEB_URLS; movementMethod = LinkMovementMethod.getInstance() }

val EditText.changeFlow
    get() = callbackFlow {
        val watcher = object : TextWatcher {
            override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
            }

            override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
            }

            override fun afterTextChanged(s: Editable?) {
                s?.let { trySendBlocking(s.toString()) }
            }
        }
        addTextChangedListener(watcher)
        awaitClose { removeTextChangedListener(watcher) }//在 flow 被 close 时调用，可以清理资源，一般必须要有
    }

val EditText.textNewFlow
    get() = callbackFlow {
        val watcher = object : TextWatcher {
            override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
            }

            override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
                s?.let { trySend(s.substring(start, start + count)) }
            }

            override fun afterTextChanged(s: Editable?) {
            }
        }
        addTextChangedListener(watcher)
        awaitClose { removeTextChangedListener(watcher) }//在 flow 被 close 时调用，可以清理资源，一般必须要有
    }


fun Activity.setText(view: View, @IdRes resId: Int, txt: String?) {
    view.findViewById<TextView>(resId)?.let {
        it.text = txt ?: ""
    }
}

fun Activity.setSelected(view: View, @IdRes resId: Int, selected: Boolean) {
    view.findViewById<View>(resId)?.let {
        it.isSelected = selected
    }
}


fun <T : View> Activity.getView(view: View, @IdRes resId: Int): T {
    val result = view.findViewById(resId) as? T
    checkNotNull(result) { "No view found with id $resId" }
    return result
}

/**
 * 输入框有字符变化，防抖300ms
 * @param debounce 防抖时间，默认300ms
 */
@OptIn(FlowPreview::class)
fun EditText.onChange(debounce: Long = 300, block: suspend (String) -> Unit) = lifecycleScope?.launch { changeFlow.debounce(debounce).collectLatest { block(it) } }

/**
 * 输入框有字符变化，防抖300ms
 */
@OptIn(FlowPreview::class)
val EditText.onChange get() = changeFlow.debounce(300)

fun CompoundButton.onUserCheck(block: CompoundButton.(Boolean) -> Unit) = setOnCheckedChangeListener { buttonView, isChecked -> if (buttonView.isPressed) block(isChecked) }

/**
 * 判断是否打开了软键盘
 */
fun View.onSoftChange(block: (Boolean) -> Unit) {
    viewTreeObserver.addOnGlobalLayoutListener {
        val r = Rect()
        rootView.getWindowVisibleDisplayFrame(r)
        val hd = rootView.height - (r.bottom - r.top)
        block(hd > 200)
    }
}

@SuppressLint("ClickableViewAccessibility")
fun View.enableDrag(ctx: Context? = null) {
    val sw = ctx?.resources?.displayMetrics?.widthPixels ?: -1
    val sh = ctx?.resources?.displayMetrics?.heightPixels ?: -1
    var cx = 0
    var cy = 0
    setOnTouchListener { _, event ->
        when (event.action) {
            MotionEvent.ACTION_DOWN -> {
                cx = event.rawX.toInt()
                cy = event.rawY.toInt()
            }

            MotionEvent.ACTION_MOVE -> {
                val currentX = event.rawX.toInt()
                val currentY = event.rawY.toInt()
                val offsetX = currentX - cx
                val offsetY = currentY - cy
                cx = currentX
                cy = currentY
                if (x + width + offsetX < sw && x + offsetX > 0 && y + height + offsetY < sh && y + offsetY > 0) {
                    x += offsetX
                    y += offsetY
                }
            }
        }
        true
    }
}

fun SeekBar.onSeekChange(block: SeekBar.(Int) -> Unit) {
    setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
        override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
            block(progress)
        }

        override fun onStartTrackingTouch(seekBar: SeekBar?) {
        }

        override fun onStopTrackingTouch(seekBar: SeekBar?) {
        }
    })
}